home *** CD-ROM | disk | FTP | other *** search
/ Power Programmierung / Power-Programmierung CD 2 (Tewi)(1994).iso / c / compiler / micro_c / mterm.c < prev    next >
C/C++ Source or Header  |  1992-02-23  |  17KB  |  676 lines

  1. /*
  2.  * MICRO-Terminal:
  3.  *
  4.  * This is a very simple communications program, which provides
  5.  * a subset ANSI (VT100) terminal emulation, and basic XMODEM
  6.  * (with checksum) file transfer.
  7.  *
  8.  * If HOTKEYS are specified on the command line, MTERM will install
  9.  * itself as a TSR (Ram-Resident) program, which can be invoked at
  10.  * any time by pressing the HOTKEYS. Available HOTKEYS are:
  11.  *        L - Left SHIFT
  12.  *        R - Right SHIFT
  13.  *        A - ALT
  14.  *        C - CONTROL
  15.  *        S - SysRq (Caution: some systems may not like this one)
  16.  *
  17.  *        EG: mterm LR    (Install with LEFT+RIGHT SHIFT for hotkeys)
  18.  *
  19.  * This demonstrates the use of the MICRO-C video interface
  20.  * and communications library functions for the IBM/PC, as well
  21.  * as the "SAVE_VIDEO", "RESTORE_VIDEO" and "TSR" functions.
  22.  *
  23.  * Copyright 1990,1992 Dave Dunfield
  24.  * All rights reserved.
  25.  */
  26. #include \mc\stdio.h        /* Standard I/O definitions */
  27. #include \mc\comm.h            /* Comm     I/O definitions */
  28. #include \mc\video.h        /* Video    I/O definitions */
  29. #include \mc\file.h            /* File        I/O definitions */
  30. #include \mc\tsr.h            /* Tsr function definitions */
  31.  
  32. /* Screen output positions */
  33. #define    SETROW        3        /* Screen row for settings display */
  34. #define MSGROW        20        /* Screen row for messages */
  35. #define    MENROW        7        /* Screen row for menu items */
  36. #define    MAICOL        0        /* Screen column for main menu */
  37. #define    SUBCOL        20        /* Screen column for sub menu */
  38. #define    FILCOL        5        /* Screen column for file prompt */
  39. #define    FILSIZ        50        /* Maximum size of file name */
  40.  
  41. /* XMODEM parameters */
  42. #define BLOCK_SIZE    128        /* size of transmit blocks */
  43. #define RETRYS        10        /* maximum number of retrys */
  44. #define    SOH_TIMEOUT    5        /* How long to wait for start of packet */
  45. #define    RX_TIMEOUT    2        /* How long in wait for chars in packet */
  46. #define    ACK_TIMEOUT    5        /* How long to wait for acknowlege */
  47.  
  48. /* Line control codes */
  49. #define SOH            0x01    /* start of header */
  50. #define ACK            0x06    /* Acknowledge */
  51. #define NAK            0x15    /* Negative acknowledge */
  52. #define CAN            0x18    /* Cancel */
  53. #define EOT            0x04    /* end of text */
  54.  
  55. /* Menu text tables (Used by 'vmenu') */
  56.     char *main_menu[] = {
  57.         "Terminal Emulation",
  58.         "XMODEM Download",
  59.         "XMODEM Upload",
  60.         "Serial port config",
  61.         "Exit to DOS",
  62.         0 };
  63.  
  64.     char *setup_menu[] = {
  65.         "Comm port",
  66.         "Baudrate",
  67.         "Data bits",
  68.         "Parity",
  69.         "Stop bits",
  70.         "Xon/Xoff",
  71.         0 };
  72.  
  73. /* Uart configuration static data tables */
  74.     unsigned baudvalue[] =
  75.         { _110,  _300,  _1200,  _2400,  _4800,  _9600,  _19200,  _38400 };
  76.     char *baudtext[] =
  77.         { "110", "300", "1200", "2400", "4800", "9600", "19200", "38400", 0 };
  78.     char *databits[] = { "Five", "Six", "Seven", "Eight", 0 };
  79.     char *parity[] = { "Odd", "Even", "Mark", "Space", "None", 0 };
  80.     char *onetwo[] = { "One", "Two", 0 };
  81.     char *flowctrl[] = { "Disabled", "Enabled", 0 };
  82.  
  83. /* Communications configuration parameters */
  84.     int comm = 0, baud = 5, data = 3, par = 4, stop = 0, flow = 1;
  85.  
  86. /* Misc global variables */
  87.     setup_selection = 0, transfer_selection = 0;
  88.     char dfile[FILSIZ+1] = "", ufile[FILSIZ+1] = "";
  89.  
  90. /* Saved video screens, attributes & cursor position */
  91.     char sav_buffer[(25*80)*2], sav_attr;
  92.     int sav_xy;
  93.     char video_save_area[SCR_BUF];
  94.  
  95. /*
  96.  * Main terminal program menu
  97.  */
  98. tty_main()
  99. {
  100.     int i;
  101.  
  102.     i = 0;            /* Default to top of menu */
  103.     save_video(video_save_area);
  104.  
  105. redraw:
  106.     draw_title();
  107.     vdraw_box(0, SETROW, 79, 2);
  108.     show_settings();
  109.  
  110.     for(;;) {
  111.         message("Select function and press ENTER");
  112.         if(vmenu(MAICOL, MENROW, main_menu, 0, &i))
  113.             continue;
  114.         switch(i) {
  115.             case 0 :        /* Terminal Emulation */
  116.                 if(!open_comm(flow))
  117.                     break;
  118.                 vcursor_line();
  119.                 restore_screen();
  120.                 ansi_term();
  121.                 save_screen();
  122.                 vcursor_off();
  123.                 goto redraw;
  124.             case 1 :        /* Download a file */
  125.                 if(open_comm(0))
  126.                     download(dfile);
  127.                 break;
  128.             case 2 :        /* Upload a file */
  129.                 if(open_comm(0))
  130.                     upload(dfile);
  131.                 break;
  132.             case 3 :        /* Setup serial port */
  133.                 setup();
  134.                 break;
  135.             case 4 :        /* Exit to DOS */
  136.                 Cclose();
  137.                 restore_video(video_save_area);
  138.                 return; } }
  139. }
  140.  
  141. /*
  142.  * Open a file for read or write (with overwrite prompt)
  143.  */
  144. HANDLE openf(fname, rw)
  145.     char *fname, rw;
  146. {
  147.     char c, omsg[80], *mode;
  148.     HANDLE fh;
  149.  
  150.     mode = "read";
  151.     fh = open(fname, F_READ);        /* First try and read the file */
  152.     if(rw) {                    /* If writing the file */
  153.         mode = "write";
  154.         if(fh) {
  155.             close(fh);
  156.             sprintf(omsg, "Overwrite existing %s (Y/N) ?", fname);
  157.             message(omsg);
  158.             do {
  159.                 c = toupper(vgetc());
  160.                 if((c == 0x1B) || (c == 'N'))
  161.                     return 0; }
  162.             while(c != 'Y'); }
  163.         fh = open(fname, F_WRITE); }
  164.     if(!fh) {
  165.         sprintf(omsg,"Cannot %s %s (Press ENTER)", mode, fname);
  166.         message(omsg);
  167.         while(vgetc() != '\n'); }
  168.     return fh;
  169. }
  170.  
  171. /*
  172.  * Open comm port with correct settings
  173.  */
  174. open_comm(flow)
  175.     char flow;
  176. {
  177.     int mode;
  178.  
  179.     /* Calculate the communications parameter value */
  180.     mode =    ((par << 4) & 0x30) |    /* parity type */
  181.             (data & 0x03) |            /* # data bits */
  182.             ((stop << 2) & 0x04) |    /* # stop bits */
  183.             ((par < 4) << 3);        /* parity enable */
  184.  
  185.     /* Open the communications port */
  186.     if(Copen(comm+1, baudvalue[baud], mode, SET_DTR|SET_RTS|OUTPUT_2)) {
  187.         message("Cannot open COM port (Press ENTER)");
  188.         while(vgetc() != '\n');
  189.         return 0; }
  190.  
  191.     /* Remove transparency if XON/XOFF flow control */
  192.     disable();
  193.     Cflags = (flow) ? Cflags & ~TRANSPARENT : Cflags | TRANSPARENT;
  194.     enable();
  195.  
  196.     return -1;
  197. }
  198.  
  199. /*
  200.  * Draw the title  header
  201.  */
  202. draw_title()
  203. {
  204.     vopen();
  205.     V_ATTR = REVERSE;
  206.     vdraw_box(0, 0, 79, 2);
  207.     vgotoxy(1, 1);
  208.     vputf("", 26);
  209.     vputf("MICRO-Terminal Version 1.2", 52);
  210.     V_ATTR = NORMAL;
  211.     vcursor_off();
  212. }
  213.  
  214. /*
  215.  * Draw the file transfer information box
  216.  */
  217. info_box(mode, filename)
  218.     char *mode, *filename;
  219. {
  220.     vdraw_box(SUBCOL, MENROW+1, 50, 8);
  221.     vgotoxy(SUBCOL+2, MENROW+3);
  222.     vprintf("%-19s: %s", mode, filename);
  223.     vgotoxy(SUBCOL+2, MENROW+5);
  224.     vputs("Blocks transferred : 0");
  225.     vgotoxy(SUBCOL+2, MENROW+7);
  226.     vputs("Transfer status    : ");
  227.     message("File transfer in progress (ESCAPE to abort)");
  228. }
  229.  
  230. /*
  231.  * Update the transfer status field
  232.  */
  233. transfer_status(text)
  234.     char *text
  235. {
  236.     vgotoxy(SUBCOL+23, MENROW+7);
  237.     vputf(text,10);
  238. }
  239.  
  240. /*
  241.  * Show the current COM port settings
  242.  */
  243. show_settings()
  244. {
  245.     vgotoxy(18, SETROW+1);
  246.     vprintf("COM%u: %5s,%2d,%5s,%2d  Xon/Xoff %-8s",
  247.         comm+1, baudtext[baud], data+5, parity[par], stop+1, flowctrl[flow]);
  248. }
  249.  
  250. /*
  251.  * Display a message
  252.  */
  253. message(ptr)
  254.     char *ptr;
  255. {
  256.     vgotoxy(0, MSGROW);
  257.     vcleos();
  258.     vmessage(38 - strlen(ptr)/2, MSGROW, ptr);
  259. }
  260.  
  261. /*
  262.  * Save the MICRO-TERMINAL video screen.
  263.  */
  264. save_screen()
  265. {
  266.     sav_xy = V_XY;
  267.     sav_attr = V_ATTR;
  268.     copy_seg(get_ds(), sav_buffer, V_BASE, 0, (25*80)*2);
  269. }
  270.  
  271. /*
  272.  * Restore the MICRO-TERMINAL video screen
  273.  */
  274. restore_screen()
  275. {
  276.     copy_seg(V_BASE, 0, get_ds(), sav_buffer, (25*80)*2);
  277.     V_ATTR = sav_attr;
  278.     V_XY = sav_xy;
  279.     vupdatexy();
  280. }
  281.  
  282. /*
  283.  * Comm port setup menu handler
  284.  */
  285. setup()
  286. {
  287.     message("Select setting (ESCAPE to cancel)");
  288.     for(;;) {
  289.         show_settings();
  290.         if(vmenu(SUBCOL, MENROW+1, setup_menu, 0, &setup_selection))
  291.             return;
  292.         switch(setup_selection) {
  293.             case 0 :    /* Comm port */
  294.                 vmenu(SUBCOL+11,MENROW+2,onetwo,-1,&comm);
  295.                 break;
  296.             case 1 :    /* baudrate */
  297.                 vmenu(SUBCOL+11,MENROW+2,baudtext,-1,&baud);
  298.                 break;
  299.             case 2 :    /* Data bits */
  300.                 vmenu(SUBCOL+11,MENROW+2,databits,-1,&data);
  301.                 break;
  302.             case 3 :    /* Parity */
  303.                 vmenu(SUBCOL+11,MENROW+2,parity,-1,&par);
  304.                 break;
  305.             case 4 :    /* Stop bits */
  306.                 vmenu(SUBCOL+11,MENROW+2,onetwo,-1,&stop);
  307.                 break;
  308.             case 5 :    /* Flow control */
  309.                 vmenu(SUBCOL+11,MENROW+2,flowctrl,-1,&flow); } }
  310. }
  311.  
  312. /*
  313.  * ANSI (VT100) Function key translation table
  314.  */
  315.     char *ansi_keys[] = {
  316.         "\x1B[A", "\x1B[B", "\x1B[D", "\x1B[C",    /* Arrow keys */
  317.         "\x1BOR", "\x1BOS", "\x1BOP", "\x1BOQ",    /* PgUp, Pgdn, Home, End */
  318.         "\x1BOM", "\x1BOm", "\x1BOp",            /* Keypad '+','-' Insert */
  319.         "\x7F",   "\x08",                        /* Delete & Backspace */
  320.         "\x1BOq", "\x1BOr", "\x1BOs", "\x1BOt",    /* F1, F2, F3 & F4 */
  321.         "\x1BOu", "\x1BOv", "\x1BOw", "\x1BOx",    /* F5, F6, F7 & F8 */
  322.         "\x1BOy", "\x1BOz",                        /* F9 & F10 */
  323.         "\x1BOl", "\x1BOn", 0, 0 };        /* Control: Pgup, Pgdn, Home, End */
  324.  
  325. /*
  326.  * Terminal mode using ANSI (VT100) emulation
  327.  */
  328. ansi_term()
  329. {
  330.     char c, xy_flag, *ptr;
  331.     unsigned x, y, state, value, parm, parms[5];
  332.  
  333.     xy_flag = -1;        /* Force initial cursor update */
  334.     state = 0;            /* Not receiving a control sequence */
  335.     for(;;) {
  336.         /* Process any input from the comm port */
  337.         if((c = Ctestc()) != -1) {
  338.             xy_flag = -1;
  339.             if(c == 0x1B) {                /* Begin escape sequence */
  340.                 state = 1;
  341.                 parms[0] = parms[1] = value = parm = 0; }
  342.             else switch(state) {
  343.                 case 1 :                /* Escape already received */
  344.                     if(c == '[') {
  345.                         state = 2;
  346.                         break; }
  347.                     state = 0;
  348.                 case 0 :                /* No special processing */
  349.                     vputc(c);
  350.                     break;
  351.                 case 2 :                /* Waiting for numeric parms */
  352.                     if(isdigit(c)) {
  353.                         value = (value * 10) + (c - '0');
  354.                         break; }
  355.                     parms[parm++] = value;    /* More to come */
  356.                     if(c == ';') {
  357.                         value = 0;
  358.                         break; }
  359.                     state = 0;
  360.                     switch(c) {
  361.                         case 'H' :        /* Cursor position (1) */
  362.                         case 'f' :        /* Cursor position (2) */
  363.                             if(y = parms[0])
  364.                                 --y;
  365.                             if(x = parms[1])
  366.                                 --x;
  367.                             vgotoxy(x, y);
  368.                             break;
  369.                         case 'J' :        /* Erase in display */
  370.                             x = V_XY;
  371.                             value ? vclscr() : vcleos();
  372.                             V_XY = x;
  373.                             break;
  374.                         case 'K' :        /* Erase in line */
  375.                             x = V_XY;
  376.                             if(value)
  377.                                 V_XY &= 0xff00;
  378.                             vcleol();
  379.                             V_XY = x;
  380.                             break;
  381.                         case 'm' :        /* Select attributes */
  382.                             x = 0;
  383.                             do {
  384.                                 V_ATTR  = (y = parms[x]) ? (y == 4) ?
  385.                                     UNDERLINE : REVERSE : NORMAL; }
  386.                             while(++x < parm); } } }
  387.         else if(xy_flag) {                /* Cursor has moved */
  388.             vupdatexy();
  389.             xy_flag = 0; }
  390.  
  391.         /* Process any input from the keyboard */
  392.         if(c = vtstc()) {
  393.             if(c & 0x80) {                /* Special function key */
  394.                 if(!(ptr = ansi_keys[c & 0x7f]))
  395.                     return;
  396.                 while(*ptr)
  397.                     Cputc(*ptr++); }
  398.             else
  399.                 Cputc((c == '\n') ? '\r' : c); } }
  400. }
  401.  
  402. /*
  403.  * Receive a file in XMODEM protocol
  404.  */
  405. download()
  406. {
  407.     char rbuffer[BLOCK_SIZE], *error_text, *old_error, ochr;
  408.     int r, rx_block_num, error_count;
  409.     HANDLE fh;
  410.  
  411.     if(vgets(FILCOL,MSGROW,"Write to file? ",dfile,FILSIZ) || !*dfile)
  412.         return;
  413.  
  414.     if(!(fh = openf(dfile, -1)))
  415.         return;
  416.  
  417.     info_box("Download to file", dfile);
  418.  
  419.     error_text = old_error = rx_block_num = 1;
  420.     error_count = RETRYS;
  421.     do {
  422.         if(vtstc() == 0x1b) {        /* Console escape */
  423.             ochr = CAN;
  424.             error_text = "CANCELED";
  425.             error_count = 0; }
  426.         else if((r = get_record(rbuffer)) == (rx_block_num & 255)) {
  427.             error_count = RETRYS;
  428.             write(rbuffer, BLOCK_SIZE, fh);
  429.             vgotoxy(SUBCOL+23, MENROW+5);
  430.             vprintf("%u", rx_block_num++);
  431.             error_text = "RX PACKET";
  432.             ochr = ACK; }
  433.         else {
  434.             switch(r) {
  435.                 case -1 :        /* Timeout */
  436.                     error_text = "TIMEOUT";
  437.                     ochr = NAK;
  438.                     break;
  439.                 case -2 :        /* Bad block */
  440.                     error_text = "BAD BLOCK#";
  441.                     while(Cgett(RX_TIMEOUT) != -1);
  442.                     ochr = NAK;
  443.                     break;
  444.                 case -3 :        /* Bad checksum */
  445.                     error_text = "BAD CHKSUM";
  446.                     ochr = NAK;
  447.                     break;
  448.                 case -4 :        /* End of file */
  449.                     error_text = "DONE";
  450.                     ochr = ACK;
  451.                     break;
  452.                 case -5 :        /* Cancel */
  453.                     error_text = "ABORTED";
  454.                     ochr = ACK;
  455.                     break;
  456.                 default:        /* Block out of sequence */
  457.                     error_text = "WRONG BLK";
  458.                     ochr = NAK; }
  459.                 --error_count; }
  460.             Cputc(ochr);
  461.             /* Update status message */
  462.             if(error_text != old_error)
  463.                 transfer_status(old_error = error_text); }
  464.         while((r > -3) && error_count);
  465.  
  466.     message("Download ended (Press ENTER)");
  467.     close(fh);
  468.     while(vgetc() != '\n');
  469.     vclear_box(SUBCOL, MENROW+1, 50, 8);
  470. }
  471.  
  472. /*
  473.  * Read a record in the XMODEM protocol, return the block number
  474.  * (0-255) if successful, or one of the following return codes:
  475.  *    -1 = Timeout
  476.  *    -2 = Bad block number
  477.  *    -3 = Bad block checksum
  478.  *    -4 = End of file
  479.  *    -5 = Canceled by remote
  480.  */
  481. get_record(rbuffer)
  482.     char rbuffer[];
  483. {
  484.     int c, i, block_num, check_sum;
  485.  
  486.     check_sum = 0;
  487.     i = -2;
  488.     switch(Cgett(SOH_TIMEOUT)) {
  489.         case SOH :        /* Receive packet */
  490.             for(;;) {
  491.                 if((c = Cgett(RX_TIMEOUT)) == -1)    /* receive timeout */
  492.                     break;
  493.                 if(i == -2)                            /* first block number */
  494.                     block_num = c;
  495.                 else if(i == -1) {                    /* second block number */
  496.                     if((255 & ~c) != block_num)
  497.                         return -2; }
  498.                 else if(i == BLOCK_SIZE)            /* checksum at end */
  499.                     return (check_sum & 0xff) == c ? block_num : -3;
  500.                 else                                /* data character */
  501.                     check_sum += (rbuffer[i] = c);
  502.                 ++i; }
  503.         case -1 :        /* timeout on waiting for packet */
  504.             return -1;
  505.         case EOT :        /* end of file encountered */
  506.             return -4;
  507.         case CAN :        /* cancel protocol */
  508.             return -5; }
  509. }
  510.  
  511. /*
  512.  * Transmit a file in XMODEM protocol
  513.  */
  514. upload()
  515. {
  516.     int i, c, tx_block_num, error_count;
  517.     char buffer[BLOCK_SIZE], *error_text, *old_error;
  518.     HANDLE fh;
  519.  
  520.     if(vgets(FILCOL,MSGROW,"Read from file? ",ufile,FILSIZ) || !*ufile)
  521.         return;
  522.  
  523.     if(!(fh = openf(ufile, 0)))
  524.         return;
  525.  
  526.     info_box("Upload from file", ufile);
  527.  
  528.     tx_block_num = old_error = error_text = 1;
  529.     error_count = RETRYS;
  530.  
  531.     /* Transmit the file data */
  532.     while(i = read(buffer, BLOCK_SIZE, fh)) {
  533.         while(i < 128)
  534.                 buffer[i++] = -1;
  535.         error_text = "TX PACKET";
  536.         while(i = send_record(buffer, tx_block_num)) {
  537.             switch(i) {
  538.                 case -1 :
  539.                     error_text = "TIMEOUT";
  540.                     break;
  541.                 case -3 :
  542.                     error_text = "RECV NAK";
  543.                     break;
  544.                 case -5 :
  545.                     error_text = "ABORTED"; }
  546.                 transfer_status(old_error = error_text);
  547.                 if((i < -3) || !error_count--)
  548.                     break; }
  549.         if(vtstc() == 0x1b) {        /* Console escape */
  550.             i = -5;
  551.             error_text = "CANCELED"; }
  552.         if(i) {                        /* Error exit */
  553.             Cputc(CAN);
  554.             break; }
  555.         error_count = RETRYS;
  556.         vgotoxy(SUBCOL+23, MENROW+5);
  557.         vprintf("%u", tx_block_num++);
  558.         if(error_text != old_error)
  559.             transfer_status(old_error = error_text); }
  560.  
  561.     /* Send the end of file indicator */
  562.     error_count = RETRYS;
  563.     if(!i) for(;;) {
  564.         Cputc(EOT);
  565.         if((c = Cgett(ACK_TIMEOUT)) == ACK) {
  566.             error_text = "DONE";
  567.             break; }
  568.         if(c == CAN) {
  569.             error_text = "ABORTED";
  570.             break; }
  571.         if(((c == -1) || (c == NAK)) && !error_count--) {
  572.             error_text = "TIMEOUT";
  573.             break; } }
  574.  
  575.     transfer_status(error_text);
  576.     message("Upload ended (Press ENTER)");
  577.     close(fh);
  578.     while(vgetc() != '\n');
  579.     vclear_box(SUBCOL, MENROW+1, 50, 8);
  580. }
  581.  
  582. /*
  583.  * Send an record in XMODEM protocol, return 0 if successful
  584.  * Otherwise, return one of the following:
  585.  *    -1 = Timeout
  586.  *    -2 = Bad block number    (N/A)
  587.  *    -3 = Bad block            (NAK RECEIVED)
  588.  *    -4 = End of file        (N/A)
  589.  *    -5 = Canceled by remote
  590.  */
  591. send_record(buffer, block_num)
  592.     char *buffer;
  593.     int block_num;
  594. {
  595.     int i, check_sum;
  596.     char *ptr;
  597.  
  598.     check_sum = 0;
  599.     ptr = buffer;
  600.     while(Ctestc() != -1);        /* purge any received data */
  601.     Cputc(SOH);
  602.     Cputc(block_num);
  603.     Cputc(~block_num);
  604.     for(i=0; i < BLOCK_SIZE; ++i) {
  605.         Cputc(*buffer);
  606.         check_sum += *buffer++; }
  607.     Cputc(check_sum);
  608.  
  609.     for(;;) switch(Cgett(ACK_TIMEOUT)) {
  610.         case ACK :            /* Packet received ok */
  611.             return 0;
  612.         case NAK :            /* Rejected */
  613.             return -3;
  614.         case CAN :            /* Remote cancel */
  615.             return -5;
  616.         case -1 :            /* Timeout */
  617.             return -1; }
  618. }
  619.  
  620. /*
  621.  * Wait for a character from the modem (with timeout)
  622.  */
  623. Cgett(timeout)
  624.     unsigned timeout;
  625. {
  626.     int h, m, s, old_s;
  627.  
  628.     if((h = Ctestc()) != -1)    /* Very fast if characters buffered */
  629.         return h;
  630.  
  631.     get_time(&h, &m, &old_s);
  632.     do {
  633.         do {
  634.             if((h = Ctestc()) != -1)
  635.                 return h;
  636.             get_time(&h,&m,&s); }
  637.         while(s == old_s);
  638.         old_s = s; }
  639.     while(--timeout);
  640.     return -1;
  641. }
  642.  
  643. /*
  644.  * Main program, either TSR or execute main tty program menu
  645.  */
  646. main(argc, argv)
  647.     int argc;
  648.     int *argv[];
  649. {
  650.     int hot_keys;
  651.     char *ptr;
  652.     vopen();
  653.     vputs("MICRO-Terminal: (Press CTRL-HOME or CTRL-END to exit):\n");
  654.     save_screen();
  655.  
  656. /* If RAM-resident, print startup message & TSR */
  657.     if(argc > 1) {
  658.         draw_title();
  659.         printf("\n\n\nCopyright 1990,1992 Dave Dunfield\nAll rights reserved.\n");
  660.         vcursor_line();
  661.         hot_keys = 0;
  662.         ptr = argv[1];
  663.         while(*ptr) switch(toupper(*ptr++)) {
  664.             case 'A' : hot_keys |= ALT;        break;
  665.             case 'C' : hot_keys |= CONTROL;    break;
  666.             case 'L' : hot_keys |= L_SHIFT;    break;
  667.             case 'R' : hot_keys |= R_SHIFT; break;
  668.             case 'S' : hot_keys |= SYS_REQ;    break;
  669.             default: abort("\nInvalid HOTKEY"); }
  670.         tsr(&tty_main, hot_keys, 2000); }
  671.  
  672. /* Not RAM-resident, execute the program */
  673.     vclscr();        /* Return to blank screen */
  674.     tty_main();
  675. }
  676.